package gateway.config.security.auth;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Sets;
import common.config.http.HttpStatusEnum;
import common.model.enums.SysAuthorityTypeEnum;
import common.model.po.SysAuthority;
import common.service.RedissonCacheService;
import common.util.CommonUrlAntPatternUtil;
import common.util.PublicUrlAntPatternUtil;
import gateway.config.security.exception.CustomAccessDeniedException;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RMap;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 顺序：2
 * 获取可以访问当前url的角色
 *
 * @author 米泽鹏
 * @since 2021-08-06 10:10 上午
 */
@Component
@Slf4j
public class CustomFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

	@Resource
	private RedissonCacheService redissonCacheService;

	/**
	 * 获取可以访问当前url的角色
	 *
	 * @param object 储存请求url信息
	 * @return null 标识不需要任何权限即可访问
	 */
	@Override
	public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
		FilterInvocation filterInvocation = (FilterInvocation) object;
		// 获取当前请求url
		String requestUrl = filterInvocation.getRequestUrl();
		if (StrUtil.isBlank(requestUrl)) {
			throw new CustomAccessDeniedException(HttpStatusEnum.BAD_REQUEST.getCode(), HttpStatusEnum.BAD_REQUEST.getMessage());
		}
		// 放行公共url
		if (PublicUrlAntPatternUtil.isPublicUrl(filterInvocation.getRequest()) || CommonUrlAntPatternUtil.isCommonUrl(filterInvocation.getRequest())) {
			return null;
		}
		// 获取缓存的权限表的数据
		List<SysAuthority> authorityList = redissonCacheService.getSysAuthorityCache();
		AntPathMatcher antPathMatcher = new AntPathMatcher();
		Set<Long> authorityIdSet = authorityList.stream().filter(authority -> SysAuthorityTypeEnum.INTERFACE.getValue().equals(authority.getType())).filter(item -> antPathMatcher.match(item.getUrl(), requestUrl)).map(SysAuthority::getId).collect(Collectors.toSet());
		if (authorityIdSet.isEmpty()) {
			// 缓存中没有找到相应url，提示路径不存在
			log.error(HttpStatusEnum.NOT_FOUND.getMessage() + "：" + requestUrl);
			throw new CustomAccessDeniedException(HttpStatusEnum.NOT_FOUND.getCode(), HttpStatusEnum.NOT_FOUND.getMessage() + "：" + requestUrl);
		} else {
			// 获取缓存的角色权限关联表的数据
			RMap<Long, Set<Long>> cacheMapByAuthId = redissonCacheService.getAllRoleIdCacheByAuthId();
			Set<String> roleIdStrSet = Sets.newHashSet();
			for (Long authId : authorityIdSet) {
				Set<Long> roleIdSet = cacheMapByAuthId.get(authId);
				if (CollUtil.isNotEmpty(roleIdSet)) {
					roleIdStrSet.addAll(roleIdSet.stream().map(String::valueOf).collect(Collectors.toSet()));
				}
			}
			if (roleIdStrSet.isEmpty()) {
				log.error("禁止访问：" + requestUrl);
				return SecurityConfig.createList(HttpStatusEnum.FORBIDDEN.name());
			}
			return SecurityConfig.createList(roleIdStrSet.toArray(new String[0]));
		}
	}

	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		return null;
	}

	@Override
	public boolean supports(Class<?> aClass) {
		return FilterInvocation.class.isAssignableFrom(aClass);
	}

}
